#include <pic16/pic18f2550.h>
#include <pic16/adc.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <usart.h>
#include "hamming.h"
#include "hd44780.h"
#include "protocol.h"
#include "kodak_protocol.h"
#include "ir_test.h"

#define DEBUG
#define WITH_AD
#define WITH_SOFT_TX
//#define WITH_TX_LCD
#define RX_TIMEOUT 8000

/**** Global Variables ****/
unsigned long timer_2, timer_2_copy;

#ifdef WITH_AD
unsigned char ad0, ad1;
#endif

unsigned char rx_data, tx_data;
unsigned char tx_bit_counter, i;
unsigned long i_long;
unsigned int serial_packet;
unsigned char serial_packet_bit_counter;


void main(void) {
	unsigned char lcd_buf[16];
	packet_t packet;
	packets_t packets;
	packets_t rx_packets;
	unsigned char frame[FRAME_LENGTH];
	unsigned long long_buf[PARAMS_LENGTH];
	cmd_t cmd;
	
	OSCCONbits.SCS = 0x0;		// System Clock Select bits = External oscillator
	OSCCONbits.IRCF = 0x7;		// Internal Oscillator Frequency Select bits 8 MHz (INTOSC drives clock directly)

#ifdef WITH_AD
	TRISAbits.TRISA0 = 1;	// ad 0
	TRISAbits.TRISA1 = 1;	// ad 1
#endif
	TRISBbits.TRISB5 = 0;	// LED as output
	TRISCbits.TRISC6 = 0;	// TX_PIN as output
	TRISCbits.TRISC7 = 1;	// RX_PIN as input
	
	timer_2 = 0;

#ifdef WITH_AD
	// set up ad
	adc_open(2, ADC_FOSC_64, ADC_CFG_2A, ADC_FRM_RJUST | ADC_INT_OFF);
#endif

	// set up interrupt and timers
	RCONbits.IPEN = 1;

	// timer 0
	T0CONbits.TMR0ON = 0;
	T0CONbits.T08BIT = 0;	// use timer0 16-bit counter
	T0CONbits.T0CS = 0;		// internal clock source
	T0CONbits.PSA = 1;		// disable timer0 prescaler
	INTCON2bits.TMR0IP = 1;	// high priority

	// timer 1
	T1CONbits.TMR1ON = 0;
	T1CONbits.RD16 = 1;
	T1CONbits.TMR1CS = 0;	// internal clock source
	T1CONbits.T1OSCEN = 0;	// dont put t1 on pin
	T1CONbits.T1CKPS0 = 1;
	T1CONbits.T1CKPS1 = 1;
	IPR1bits.TMR1IP = 0;		// low priority

	// timer 2
	T2CONbits.T2CKPS0 = 1;
	T2CONbits.T2CKPS1 = 0;
	T2CONbits.TOUTPS0 = 1;
	T2CONbits.TOUTPS1 = 0;
	T2CONbits.TOUTPS2 = 0;
	T2CONbits.TOUTPS3 = 1;
	IPR1bits.TMR2IP = 0;		// low priority
	PIR1bits.TMR2IF = 1;
	T2CONbits.TMR2ON = 1;
	PIE1bits.TMR2IE = 1;

	// timer 3
	T3CONbits.RD16 = 1;
	T3CONbits.TMR3CS = 0;	// internal clock source
	T3CONbits.T3CKPS0 = 1;
	T3CONbits.T3CKPS0 = 1;
	IPR2bits.TMR3IP = 0;		// low priority
	T3CONbits.TMR3ON = 1;
	PIE2bits.TMR3IE = 1;
	PIR2bits.TMR3IF = 1;

	INTCONbits.PEIE = 1;
	INTCONbits.GIE = 1;

	TRISAbits.TRISA2 = 1;

#ifdef WITH_TX_LCD
	lcd_init();
#endif

#ifdef WITH_TX_LCD
	if (!PORTAbits.RA2) {
#else
	if (PORTAbits.RA2) {
#endif
		// send
		tx_data = 0;
		while (1) {
			init_protocol(&packets, frame);
			timer_2_copy = timer_2;
			cmd = sync;
			packet.cmd = cmd;
			packet.params[0] = 0; //timer_2_copy;
			packet.params[1] = 0; //timer_2_copy >> 8;
			packet.params[2] = 0; //timer_2_copy >> 16;
			packet.params[3] = 0; //timer_2_copy >> 24;
			packet.params[4] = 0;
			packet.params[5] = 0;
			packet.params[6] = 0;
			packet.params[7] = 0;
			add_packet_to_protocol_encoder(&packet);

			cmd = fade_in;
			packet.cmd = cmd;
			packet.params[0] = 0;
			packet.params[1] = 1;
			packet.params[2] = 7;
			packet.params[3] = 2;
			packet.params[4] = 0;
			packet.params[5] = 0;
			packet.params[6] = 0;
			packet.params[7] = 0;
			add_packet_to_protocol_encoder(&packet);
#ifdef WITH_TX_LCD
	//		sprintf(lcd_buf, "%02x%02x%02x%02x", s[3], s[2], s[1], s[0]);
	//		debug(lcd_buf);
#endif

			encode_frame(37, 255);
#ifdef WITH_TX_LCD
			debug(frame +  7);
			sleep_ms(1000);
			debug(frame +  25);
			sleep_ms(1000);
/*
			lcd_clear();
			for (i = 0; i < 8; i++) {
				send_data(frame[i + 5]);
			}
			lcd_ddram(SECOND_LINE);
			for (i = 8; i < 16; i++) {
				send_data(frame[i + 5]);
			}
*/
//			for (i = 0; i < 4; i++) {
//				sprintf(lcd_buf, "%02x", buf[i]);
//				send_data(lcd_buf[0]);
//				send_data(lcd_buf[1]);
//			}
//			lcd_ddram(SECOND_LINE);
//			for (i = 4; i < 8; i++) {
//				sprintf(lcd_buf, "%02x", buf[i]);
//				send_data(lcd_buf[0]);
//				send_data(lcd_buf[1]);
//			}
#endif
			for (i = 0; i < FRAME_LENGTH; i++) {
#ifndef WITH_TX_LCD
				rs232_transmit(frame[i]);
#endif
			}
		}
	}
	else {
		// receive
		// set up lcd
		lcd_init();
		while (1) {
			init_protocol(&packets, frame);
			while (rs232_receive(&rx_data, RX_TIMEOUT) > 0) {
				add_to_protocol_decoder(rx_data);
				if (get_protocol_data(&rx_packets) > 0) {
					// ugly hack - copy all data to unsigned long similar data...
		//			long_buf[0] = packet_buf.params[0];
		//			long_buf[1] = packet_buf.params[1];
		//			long_buf[2] = packet_buf.params[2];
		//			long_buf[3] = packet_buf.params[3];
		//			timer_2_copy = long_buf[0] | (long_buf[1] << 8) | (long_buf[2] << 16) | (long_buf[3] << 24);
					// DEBUG we should disable timer_2 interrupt here...
		//			timer_2 = timer_2_copy;
				//	sprintf(lcd_buf, "%lu ", timer_2_copy);
				//	sprintf(lcd_buf, "%lu %02x%02x%02x%02x", timer_2_copy, buf[4], buf[5], buf[6], buf[7]);

					sprintf(lcd_buf, "%02x%02x%02x%02x%02x%02x%02x%02x", 
						rx_packets.packet[0].params[0], rx_packets.packet[0].params[1], rx_packets.packet[0].params[2], rx_packets.packet[0].params[3], 
						rx_packets.packet[0].params[4], rx_packets.packet[0].params[5], rx_packets.packet[0].params[6], rx_packets.packet[0].params[7]);
					debug(lcd_buf);
					sleep_ms(1000);

					break;
				}
			}
		}
	}
}

void rs232_transmit(unsigned char s) {
	// prepare packet
	serial_packet = 1 | (s << 2) | (1 << 10);
	serial_packet_bit_counter = 0;

	// send it
	INTCONbits.TMR0IF = 1;	/* Force Instant entry to Timer 0 Interrupt */
	T0CONbits.TMR0ON = 1;	// enable timer0
	INTCONbits.T0IE = 1;	/* Ensure that TMR0 Interrupt is enabled    */
	sleep_ms(5);			// wait for tx
}

unsigned char rs232_receive(unsigned char *s, unsigned long timeout) {
	unsigned long start_timer_2;
	usart_open(	USART_TX_INT_OFF &
				USART_RX_INT_OFF & 
				USART_BRGH_LOW & 
				USART_ASYNCH_MODE & 
				USART_EIGHT_BIT,
				133	// 9600 kbps
	);
	TXSTAbits.TXEN = 0;	// use software usart for sending data
	BAUDCONbits.RXDTP = 1;

	start_timer_2 = timer_2;
	while(!usart_drdy()) {
		// wait timeout ms for data
		if ( (((signed long)(timer_2 - start_timer_2) < 0) ? (-1 * (timer_2 - start_timer_2)) : (timer_2 - start_timer_2)) > timeout) {
			usart_close();
			return 0;
		}
	}
	*s = usart_getc();
	usart_close();
	return 1;
}

void send_space() {
	// takes 104,2 uS
	TX_PIN = 0;
}

void send_mark() {
	// takes 104,2 uS
	for (tx_bit_counter = 0; tx_bit_counter < 30; tx_bit_counter++) {
		TX_PIN ^= 1;
		__asm
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
			nop
		__endasm;
	}
}

void sleep_ms(unsigned long ms) {
	unsigned long start_timer_2;

	start_timer_2 = timer_2;
	// while the absolute value of the time diff < ms
	while ( (((signed long)(timer_2 - start_timer_2) < 0) ? (-1 * (timer_2 - start_timer_2)) : (timer_2 - start_timer_2)) < ms) {
		// do nothing
	}
}

static void phase_control(void) __interrupt 1 {
	if (INTCONbits.TMR0IF) {	// 416,6 us delay at 20 MHz
		TMR0H = (unsigned char)(TIMER0_RELOAD >> 8);
		TMR0L = (unsigned char)TIMER0_RELOAD;	/* Reload the Timer ASAP */
		INTCONbits.TMR0IF = 0;	/* Clear the Timer Flag  */

		if (serial_packet & (1 << serial_packet_bit_counter)) {
			send_mark();
		}
		else {
			send_space();
		}
		serial_packet_bit_counter++;
		if (serial_packet_bit_counter >= 11) {
			serial_packet_bit_counter = 0;
			T0CONbits.TMR0ON = 0;
			INTCONbits.T0IE = 0;
		}
	 }
}

static void timer_control(void) __interrupt 2 {
	if (PIR1bits.TMR2IF) {
		PR2 = TIMER2_RELOAD;		// 1 ms delay at 8 MHz
		PIR1bits.TMR2IF = 0;
		timer_2++;
	}
	if (PIR2bits.TMR3IF) {
		TMR3H = (unsigned char)(TIMER3_RELOAD >> 8);	// 8 ms delay at 8 MHz
		TMR3L = (unsigned char)TIMER3_RELOAD;
		PIR2bits.TMR3IF = 0;	/* Clear the Timer Flag  */

#ifdef WITH_AD
		// AD0
		adc_setchannel(0);
		adc_conv();
		while(adc_busy()) {
			// wait
		}
		ad0 = 0xff - (adc_read() >> 2);		// potmetrene vender omvendt på boardet

		// AD1
		adc_setchannel(1);
		adc_conv();
		while(adc_busy()) {
			// wait
		}
		ad1 = 0xff - (adc_read() >> 2);		// potmetrene vender omvendt på boardet
#endif
	}
}

void debug_led(unsigned char s) {
	unsigned char i;
	for (i = 0; i < 8; i++) {
		if ((s & (1 << i)) >> i) {
			LED_PIN = 1;
			sleep_ms(560);
			LED_PIN = 0;
			sleep_ms(40);
		}
		else {
			LED_PIN = 1;
			sleep_ms(40);
			LED_PIN = 0;
			sleep_ms(560);
		}
	}
}

void debug(unsigned char *s) {
	unsigned char i;

	lcd_clear();
	for (i = 0; i < 8; i++) {
		if (!s[i]) {
			return;
		}
		send_data(s[i]);
	}

	lcd_ddram(SECOND_LINE);

	for (i = 0; i < 8; i++) {
		if (!s[i + 8]) {
			return;
		}
		send_data(s[i + 8]);
	}
}

void to_binary_str(unsigned char *buf, unsigned char n) {
	unsigned char i;
	for (i = 0; i < 8; i++) {
		if (n & (1 << i)) {
			buf[i] = '1';
		}
		else {
			buf[i] = '0';
		}
	}
	buf[8] = 0;
}

